Table of Contents
RECURSION
Computer Memory
Conditional check
non iterative factorial funtion
Recursive versus Iterative
Sierpiński triangle
Now we will plot this in efficient way
Greatest Commen Deviser (GCD)
Assignment
Problem 1: Digit Summation
Problem 2: Maximum Element
Variable Number of Arguments
Assignment
Problem 1: Name-value Pairs
Problem 2: Data Entry
Function Handles and Nested Functions
Anonymous function
Nested Function
Assignment
Problem 1: Autograder
Problem 2: Fun with polynomial
Mixed Mode Arithmetic
Script examples of binary arithmetic operations
Assignment
Computer Memory
Conditional check
non iterative factorial funtion
Recursive versus Iterative
Sierpiński triangle
Now we will plot this in efficient way
Greatest Commen Deviser (GCD)
Assignment
Problem 1: Digit Summation
Problem 2: Maximum Element
Variable Number of Arguments
Assignment
Problem 1: Name-value Pairs
Problem 2: Data Entry
Function Handles and Nested Functions
Anonymous function
Nested Function
Assignment
Problem 1: Autograder
Problem 2: Fun with polynomial
Mixed Mode Arithmetic
Script examples of binary arithmetic operations
Assignment
RECURSION
ecursion is a powerful idea that makes it possible to solve some problems easily that would otherwise be quite difficult. Recursion cannot be used in all programming languages, but it is supported by most modern languages, including MATLAB.
A definition of a concept that uses the concept itself is called a recursive definition, and the use of a concept in the definition of the concept is called recursion.
% function f=rfact(n)
% if n==0
% f=1
% else
% f=n*rfact(n-1)
% end
% end
rfact(3)
Computer Memory
Local variable stored in Stack. we can iamgige like piles nof dishes (e.g. in rfact function n,f)
once it completed step by step it start coming back. in this process variables are storing in stack.
rfact(0)
rfact(1)
rfact(5)
% rfact(2.5)
%out of memory. infinite recursion
so now we have check condition for non negative scaler integer.
help fix
help isscalar
% function f=rfact(n)
% if ~isscalar(n) || n~=fix(n) || n<0
% error("non negative scaler integer input expected")
% end
% if n==0
% f=1;
% else
% f=n*rfact(n-1);
% end
% end
rfact(2.5)
Error using rfact (line 3)
non negative scaler integer input expected
rfact(5)
rfact(600)
rfact(50000)
% rfact(69899)
rfact(69899)
Out of memory. The likely cause is an infinite recursion within the program.
Error in rfact (line 8)
f=n*rfact(n-1);
it means there not enought memory in stack to carry out.
Conditional check
we can an conditional check, right click on 2- and add condtional check.
rfact(4)
2 if ~isscalar(n) ||n~=fix(n) || n<0
we can see stack, and run code linr by line and can variable change in workspace.
End debugging session by clicking Quit Dbugging
non iterative factorial funtion
% function f=ifact(n)
% if ~isscalar(n) || n~=fix(n) || n<0
% error("non negative scaler integer input expected")
% end
% f=1;
% for ii=1:n
% f=ii*f;
% end
% end
ifact(3)
ifact(0)
ifact(69899)
%rfact(69899)
rfact(69899)
Out of memory. The likely cause is an infinite recursion within the program.
Error in rfact (line 8)
f=n*rfact(n-1);
ifact(69899)
ans =
Inf
So, here we can see iterative algorithm is better.
Recursive versus Iterative
- Every recursive implementation has an iterative version.
- iterative is almost always faster than recursive.
- But often the recursion version is easier to implement.
Sierpiński triangle
help clf
help axis
s = 1; c = [0;0]; % s = length of side, c = center
clf
axis([c(1)-s/2,c(1)+s/2,c(2)-s/2,c(2)+s/2],'equal')
plot(c(1)-[s,0,-s,s]/2,c(2)-[s,-s,s,s]*sqrt(3)/4,'k')
plot([1,5,2,3,5,0.5])
plot([1,2,3,1],[3/4,3,3/4,3/4])
plot(c(1)-[s,0,-s,s]/2,c(2)-[s,-s,s,s]*sqrt(3)/4)
plot([s,0,-s,s]/2,[s,-s,s,s]*sqrt(3)/4)
plot(-[s,0,-s,s]/2,-[s,-s,s,s]*sqrt(3)/4)
plot([-0.5,0,0.5,-0.5],[-0.5,0.5,-0.5,-0.5])
3.^[1:3]
% function sierpinski(depth)
% s = 1; c = [0;0]; % s = length of side, c = center
% clf; axis([c(1)-s/2,c(1)+s/2,c(2)-s/2,c(2)+s/2],'equal');
% title(sprintf('Sierpinski Triangle with Depth = %d', depth))
% hold on;
% sier(depth, s, c);
% hold off;
% end
% function sier(d, s, c)
% if d <= 0 % base case
% plot(c(1)-[s,0,-s,s]/2,c(2)-[s,-s,s,s]*sqrt(3)/4,'k');
% else % recursive case
% s = s/2; % cuts size in half
% h = s*sqrt(3)/2; % height
% sier( d-1, s, c - [s;h]/2 ); % bottom left
% sier( d-1, s, c + [0;h]/2 ); % top middle
% sier( d-1, s, c + [s;-h]/2 ); % bottom right
% end
% end
sierpinski(8)
No of time sieri function is called
so in depth 8 calculation-- 9841 time
sum(3.^(0:8))
No. of smallest triengl=3^depth=3^8=6561
3^8
Max height stack reaches here is=(depth)+1=9
sierpinski(1);
for d=[1:7];
t0=cputime;
sierpinski(d);
t(d)=cputime-t0
end
plot(t);
grid on
Now we will plot this in efficient way
% function sierpinskiE(depth)
% s = 1; c = [0;0]; % s = length of side, c = center
% clf; axis([c(1)-s/2,c(1)+s/2,c(2)-s/2,c(2)+s/2],'equal');
% title(sprintf('Sierpinski Triangle with Depth = %d', depth))
% hold on;
% plot(1/2*[-1,0,1,-1],sqrt(3)/4*[-1,1,-1,-1],'r--');
% pts = sier(depth, s, c, []);
% plot(pts(1,:),pts(2,:),'k-');
% hold off;
% end
% function pts = sier(d, s, c, pts)
% if d <= 0 % base case
% pts = [pts, c + [[-s,0,s,-s,nan]/2;sqrt(3)*[-s,s,-s,-s,nan]/4]];
% else % recursive case
% s = s/2; % cuts size in half
% h = s*sqrt(3)/2; % height
% pts = sier( d-1, s, c - [s;h]/2, pts ); % bottom left
% pts = sier( d-1, s, c + [0;h]/2, pts ); % top middle
% pts = sier( d-1, s, c + [s;-h]/2, pts ); % bottom right
% end
% end
sierpinskiE(7)
in 1st funcrtion sierpinski it is not possible to plot to depth 12 as computes hanged.
but sierpinskiE we can easily plot to depth 12 in less time.
sierpinskiE(1);
for d=[1:12];
t0=cputime;
sierpinskiE(d);
t(d)=cputime-t0
end
plot(t);
grid on
Greatest Commen Deviser (GCD)
We studies two recursive example
Now lts understand this,
% function d= rgcd(x, y)
% if (~isscalar(x) || x~=fix(x) || x<0 || ...
% ~isscalar(y) || y~=fix(y) || y<0)
% error("X, Y must be non negative scalar");
% end
% a=max([x,y]);
% b=min([x,y]);
% if b==0;
% d=a;
% else
% d=rgcd(b,rem(a,b));
% end
% end
rgcd(21,14)
Iterative version of GCD
% function d= igcd(x, y)
% if (~isscalar(x) || x~=fix(x) || x<0 || ...
% ~isscalar(y) || y~=fix(y) || y<0)
% error("X, Y must be non negative scalar");
% end
% a=max([x,y]);
% b=min([x,y]);
% while b~=0;
% r=rem(a,b);
% a=b;
% b=r;
% end
% d=a;
% end
igcd(102,54)
Assignment
Problem 1: Digit Summation
Write a recursive function to sum the individual digits of an input number.
my solution
% digit summation code
% function s=digit_sum(n)
% if (~isscalar(n) || n~=fix(n) || n<0)
% error("n must be non negative scalar");
% end
%
% if n==0;
% s=0;
% else
% s=digit_sum(fix(n*0.1))+n-(fix(n*0.1))*10;
% end
%
% end
digit_sum(12345)
Coursera solution
% function res = cdigit_sum(n)
% if n < 10
% res = n;
% else
% res = cdigit_sum(floor(n/10)) + rem(n,10);
% end
% end
cdigit_sum(12345)
Problem 2: Maximum Element
Write a recursive function to find the maximum element in a vector.
my solution
v=[1,32,2,58,-55,0,12];
n=length(v)
v(1)
v(2:n)
length(v(2:n))
numel(v)
Using max function
% function m=max_in(v)
%
% n=length(v);
%
% if n==1;
% m=v(1);
% else
% m = max(v(1),max_in(v(2:n)));
% end
%
%
% end
max_in(v)
without using max function
% function m=cmax_in(v)
%
% n=length(v);
%
% if n==1;
% m=v(1);
% else
% m = cmax_in(v(2:n));
% if v(1)>m
% m = v(1);
% end
%
% end
cmax_in(v)
Solution by coursera
% function mx = recursive_max(v)
% if length(v) == 1
% mx = v(1);
% else
% % Each recursion, v(2:end) becomes smaller by 1-element
% mx = bigger(v(1),recursive_max(v(2:end)));
% end
% end
% % Cannot use the max function. Use helper function to return the larger of
% % two element
% function c = bigger(a,b)
% c = a;
% if a < b
% c = b;
% end
% end
recursive_max(v)
Variable Number of Arguments
help nargin
help nargout
help varargin
help varagout
help find
v=[1,0,32,2,58,-55,0,2];
find(v==2)
find(v==58)
find(v==0)
find(v==9) % not in list
find(v) %in ans 2, 6 is missing as it is zero
find(v,3) %in ans 2 is missing as it is zero
help rng
fprintf can handle unlimited no of argument, we can also erite function which can handle unlimited no of argument.
Function that cant handle unlimited no of argument
% function index = find_first(v,e)
% if nargin == 0
% error('At least one argument is required')
% elseif nargin == 1
% e = 0;
% end
% index = 0;
% indices = find(v == e);
% if ~isempty(indices)
% index = indices(1);
% end
% end
rng(0);
v=randi([-3,3],1,12)
find_first(v,1) %one is at index 5
find_first(v,2)
find_first(v,12) %not in v so 0
find_first(v,0)
find_first(v) %2nd argumnet not given so by default take 0
Enters multiple argument in fprintf via varargin-
varargin can take maltiple argument
% function print_all(varargin)
% for ii = 1:nargin
% fprintf('Here is input argument number %d: %d\n', ii, varargin{ii})
% end
% end
print_all(pi)
print_all(7,-3)
print_all(4,5,6,8,-3,8)
help fprintf
help sprintf
sprintf('the first three positive integer are %d,%d and %d',1,2,3)
We are goint to write a function as,
- in this 1st argument store in format and rest of argument in varargin.
- logical variablr skip is used when it encounter % sign.
% function out = print_num(format,varargin)
% out = '';
% argindex = 1;
% skip = false;
% for ii = 1:length(format)
% if skip
% skip = false;
% else
% if format(ii) ~= '%'
% out(end+1) = format(ii);
% else
% if ii+1 > length(format)
% break;
% end
% if format(ii+1) == '%'
% out(end+1) = '%';
% else
% if argindex >= nargin
% error('not enough input arguments');
% end
% out = [out num2str(varargin{argindex},format(ii:ii+1))];
% argindex = argindex + 1;
% end
% skip = true;
% end
% end
% end
% end
print_num('the first three positive integer are %d,%d and %d',1,2,3)
pct=20;
a=934.40;
print_num('%d%% of %f is %f',pct, a, a*pct/100)
% function varargout = distribute(v)
% for ii = 1:length(v)
% varargout{ii} = v(ii);
% end
% end
[a,b,c]=distribute([14,-pi,0])
[a,b]=distribute([14,-pi,0])
%[a,b,c]=distribute([14,-pi,0,g]) error will come
One last detail that we should mention is that, just as with varargin, you can include normal local variables along with varargout in the list of formal output arguments in the function signature. But as with varargin, varargout must be the last argument in the list. In analogy to varargin, varargout holds any excess output arguments that come after the normal ones.
All right, we've relearned what nargin and nargout do, and we've learned what varargin and varargout do. The first two count arguments, and the second two store variable numbers of arguments. That's how MATLAB allows you to define functions that can handle unlimited numbers of arguments.
Assignment
Problem 1: Name-value Pairs
Write a function that pairs up the keys and the respective values from unspecified number of input
% % Problem 1: Name-value Pairs
% % Write a function that pairs up the keys and the
% % respective values from unspecified number of input
%
% function db = name_value_pairs(varargin)
%
% if nargin>0 && rem(nargin, 2) == 0
% db = cell(nargin/2, 2);
%
%
% for ii = 1:nargin
% if rem(ii,2) ~= 0
% if ischar(varargin{ii})
% db{(ii+1)/2,1} = varargin{ii};
% else
% db ={};
% return;
% end
% else
% db{ii/2,2} = varargin{ii};
% end
%
%
% end
% else
% db = {};
% end
% end
name_value_pairs('name','John Smith','age',32,'children',{'Joe','Jill'})
Problem 2: Data Entry
Write a function that keys in new voters' information into the current database.
% function database = voters(database,varargin)
% % Get the length of the input database
% count = length(database);
%
% % Create a copy of the database. This will be the new database if input
% % is valid
% tmp = database;
%
% % Names and IDs come in pairs. Increment loop counter by 2
% for ii = 1:2:length(varargin)
% % Make sure the Name is a char or string
% if ischar(varargin{ii}) || isstring(varargin{ii})
% count = count + 1;
% tmp(count).Name = string(varargin{ii});
% % Make sure there is a valid ID
% if ii+1 <= length(varargin) && isnumeric(varargin{ii+1}) && round(varargin{ii+1}) == varargin{ii+1}
% tmp(count).ID = varargin{ii+1};
% else
% % Not valid input. Return original database
% return;
% end
% else
% % Not valid input. Return orginal database
% return;
% end
% end
% % All inputs valid. Update database.
% database = tmp;
% end
database = voters([], 'Adam', 11111)
database = voters(database, 'Eve', 22222)
database(1)
database(2)
Function Handles and Nested Functions
- A variable that identifies a function is called a function handle, and there are some cases in which function handles are useful.
For example, some functions can take other functions as arguments. The integral function, for instance, uses numerical approximation to estimate the integral of any function supplied as an input argument. Another case will come up in a couple of weeks when we'll show you how to create a graphical user interface, and you'll learn that you can specify which function should be called when various buttons are clicked. These are termed callback functions and you specify them using function handles
- There are two even more advanced cases. You can actually create a one-line function without a name, called appropriately enough, an anonymous function. The only way to call it is through a function handle. Finally, function handles allow function inside one M file to call a sub-function inside another M file.
- Nested functions which are similar to sub-functions, but are defined inside other functions.
trig=@sin
x=trig(pi/2)
plot(trig(0:0.01:2*pi))
close all
x=trig
This time, MATLAB saw that we did not include an input argument, so instead of using the function handle to call the sin function and copying the return value into X, you just copy the function handle itself into X. So now we can call a sin function with X.
x(pi/2)
But what about functions that you call with no arguments? Like the function Pi which may not seem like a function but actually is a function, a built-in function. It just doesn't look like a function because it takes no input arguments and returns just one output argument. Let's assign a function handle for Pi to the variable mypi.
x=pi
mypi=@pi
Now let's try to use it to give X the value Pi again. Well, that didn't work.
x=mypi
To call the function, just include empty parentheses like this,
y=mypi()
If you want to put together an array of several function handles, you can use regular arrays
xpt={@sin @cos @plot}
xpt{2}(0)
Calling the function through cell array indexing might look a little bit strange at first. This command calls the cosine function with an input argument of zero.
xpt{1}(pi/2)
It first uses curly braces to select the appropriate element of the cell array and then uses regular parentheses to provide the input arguments to the selected function.
Here's a command "impress your friends."
xpt{3}(xpt{1}(-pi:0.01:pi))
We just called plot by putting a three in curly braces to draw the sign by putting a one in curly braces from minus pi to pi.
help fplot
fplot(@sin,[0 2*pi])
but you might argue that we could do the same thing without a function handle, a good old-fashioned way with plot.
plot(0:0.01:2*pi, sin(0:0.01:2*pi))
Sure, but here we had to provide the actual vector of x values twice while fplot does all that for us, and it gets better, look at this example.
fplot(@tan,[0,pi])
now, lets plot this via plot function
plot(0:0.01:pi, tan(0:0.01:pi))
not so good, now let change the resolution
plot(0:0.001:pi, tan(0:0.001:pi))
still not good.
now we can understand why fplot is important.
Anonymous function
Let's say we have a polynomial expression that we need to use a few times in our program. We could create a sub function, but it would be overkill to do that for a single expression, not to mention that we'd need to make up a meaningful name for the function. Instead, MATLAB allows us to create a so-called anonymous function as a single expression and return a function handle to it all in one fell swoop.
- The only rule is that the anonymous function definition can't be inside a control structure such as an if statement, a switch statement, or a loop, but it can be anywhere else in a function, or a script, or the command window. Here's an example of the definition of an anonymous function, which happens to be a four-term polynomial.
poly=@(x) 2*x.^3-x.^2+2*x-12;
poly(1)
poly(0:5)
plot(-10:10,poly(-10:10))
fplot(poly,[-10,10])
xnf=@(x,y) x+y;
xnf(1,2)
x=1000
y=2000
xnf(11,12)
x,y
Here we first gave x and y the values 1,000 and 2,000. Then we called xfn, giving its local variables with the same names x and y, the values 10 and 12. After the function return, we checked x and y and found that they still have the same values that they had before the function was called. So the variables outside the function are not accessible inside the function if they have the same names as one or the arguments.
c=10;
f=@(x) c*x;
f(3)
If it were a global reference to c, then if we change c to 11 and rerun the function, we'd get 33, which is exactly what would happen if f were the handle of an ordinary function that made a global reference to c. But that is not what happens for an anonymous function.
Here we change c from 10 to 11, but the function did not change. It's still multiplied its input argument by 10.
c=11;
f(3)
You can even remove c from the workplace and it'll still have no effect on our function.
clear c
f(3)
Aanonymous function construct is called anonymous, which means literally not having a name. Didn't we give the name f to the anonymous function that we just defined, for example? Well, no. We put the handle for this function into a variable named f, but the function itself had no name. Contrast that with the first example we gave of a function handle.
trig=@sin
In that case, the name of the function is sine and trig is the name of the variable that holds the handle for sine. We can drive the anonymity of an anonymous function home by using f plot again.Here we've given f plot an anonymous function whose expression is x plus the sine of x.
fplot(@(x) x+sin(x),[-5,5])
It plots the function from minus five to five, and yet there's no name for the function anywhere. So we've seen how to define anonymous functions and we've seen examples with one input argument and two input arguments. Of course you can have as many input arguments as you wish. Multiple output arguments are also supported, but in a very limited way. The only way to get multiple outputsis to have the expression, that is the body of the anonymous function, be a call of a function that already returns multiple arguments. Here's an example.
smax=@(A) max(A.^2)
[mx ind]=smax([1 2;3 4])
with anonymous functions, you can get multiple outputs only by calling a function that already produces multiple outputs. But you can get a lot of versatility by using the deal function
xyz=@(x,y) deal(x*y, x+y)
[p, s]=xyz(10,20)
A normal function is a function name and can have any number of statements in its body, but an anonymous function has no name, and it can have only one statement in its body. Also, if you want to have multiple outputs from an anonymous function, it's one executable statement has to be a call to a function that already returns multiple outputs.
In addition to the three kinds of functions that we've seen so far, the main function, the sub-function, and the anonymous function, there's one more function that we need to talk about, it's called the nested function.
Nested Function
it's similar to a sub-function in that it lives in an M-file with a main function, just like a sub-function does. However, a nested function is actually defined inside the body of another function, also called its parent function.
% function [y1, y2] = first_nested_example(x)
% c = 10;
% sub(c,x);
% y1 = inner(x);
% %nested function start here
% function out = inner(in)
% out = c*in;
% end
% %nested function end here
% c = 11;
% sub(c,x)
% y2 = inner(x);
% end
% %sub function start here
% function sub(in1,in2)
% fprintf('Multiplying %d times %d\n',in1,in2)
% end
% %sub function end here
A nested function has access not only to all the variables inside it, but also to variables inside its parent function. Similarly, the parent function has access not only to all the variables inside it, but also to variables inside it's nested functions.
[a1,b1]=first_nested_example(3)
take an another example
% function circle_area = assignment_rule(r)
%
% calculate_area
% fprintf('Area of circle with radius %.1f = %.1f\n',r,circle_area)
%
% function calculate_area
% circle_area = pi*r^2;
% end
% end
assignment_rule(3)
% function A
% xA = 1;
% function B
% xB = 2;
% function C
% xC = 3;
% show('C','xA',xA)
% show('C','xB',xB)
% show('C','xC',xC)
% end % C
% show('B','xA',xA)
% show('B','xB',xB);
% C
% D
% end % B
% function D
% xD = 4;
% show('D','xA',xA);
% show('D','xD',xD);
% end % D
% show('A','xA',xA)
% B
% D
% end % A
% function show(funct,name,value)
% fprintf('in %s: %s = %d\n',funct,name,value);
% end
you can see here the accessibilities of all the variables. In A, xA is accessible; in B, xA and xB are accessible; in C, xA, xB and xC are accessible, and in D, xA and xD are accessible, and D is called twice, once down here in A, and once up here in B. This second call of D brings up a visibility rule for nested functions that we haven't mentioned yet, it's the rule that tells us where a function is accessible, where it can be called. There are actually three paths for accessibility. A parent can call a child as for example here, A calls B and D, but it can't call a grandchild, so A can't call C. A sibling can call a sibling, has for example, B can call D, and a descendant can call any ancestor, so C can call B or A, for example. The sibling calling feature is interesting because of the difference from the variable sharing rule. While sibling functions can call each other, they can't access each other's local variables. Access to functions goes up and down and side to side, but access to variable goes only up and down.
A
Remember this, we set c equal to 10,
c=10;
f=@(x) c*x;
f(3)
c=11;
f(3)
clear c
f(3)
The value 10 is baked into the anonymous function, to change it, you'd have to define the anonymous function again. Let's suppose you need to do that, and you need to do it multiple times with different values of c, but suppose also that like me, you don't want to spend your valuable time defining an anonymous function over and over. Well, you can write a function to do it for you like this.
% function fh = get_anon_handle(c)
%
% fh = @(x) c*x;
%
% end
f10=get_anon_handle(10)
f11=get_anon_handle(11)
f10(3)
f11(3)
so, here via a function we can define anonymas with different c
% function fh = get_polynomial_handle(p)
%
% function polynomial = poly(x)
% polynomial = 0;
% for ii = 1:length(p)
% polynomial = polynomial + p(ii).*x.^(ii-1);
% end
% end
%
% fh = @poly;
% end
pc=get_polynomial_handle([-4,-1,3,1])
pq=get_polynomial_handle([-10,0,7])
pc(1)
pq(1)
It's just like what we saw for the anonymous function. Once you create a handle to a function, the values of it's non-local variables in this case p are baked in. So that's it for nested functions. But before we leave, let's plot pc and pq using f plot, we plot them over the range from minus 3 to 2.
fplot(pc,[-3,2]);
hold on
fplot(pq,[-3,2])
let's finish by recapping what we've learned.
- First, we learned that a nested function is a function that's defined inside another function, and that the nested function is called the child of the outer function which is called the parent.
- We learned that nesting can extend the grandchildren and in fact to any number of descendants.
- We learned about non-local scope which means that an ancestor and a descendent can share variables.
- We learned that a parent can call a child, a sibling can call a sibling, and a descendent can make a recursive call to any of its ancestors.
- Finally, we learned that a function can return a handle to a nested function.
Assignment
Problem 1: Autograder
Write a function that compare two different functions' results.
help isequal
% function f=grader(fn1,fn2, varargin)
%
% for n=1:length(varargin);
% if isequal(fn1(varargin{n}),fn2(varargin{n}));
% f=true;
% else
% f=false;
% end
% end
%
% end
grader(@sin,@max,0)
grader(@sin,@max,0,1)
grader(@cos,@cos,0,1,[-pi,0,pi])
oficial solution
*********
% function pass = cgrader(fn1,fn2,varargin)
% pass = false;
% for ii = 1:length(varargin)
% if ~isequal(fn1(varargin{ii}),fn2(varargin{ii}))
% return;
% end
% end
% pass = true;
% end
*******
Problem 2: Fun with polynomial
Mixed Mode Arithmetic
single- Single precision floating pont number.(32 bit)
double-Double precision floating pont number (64 bit)
- double: Double-precision arrays
- single: Single-precision arrays
- int8: 8-bit signed integer arrays [-128....127]
- int16: 16-bit signed integer arrays
- int32: 32-bit signed integer arrays
- int64: 64-bit signed integer arrays
- uint8: 8-bit unsigned integer arrays [0....255]
- uint16: 16-bit unsigned integer arrays
- uint32: 32-bit unsigned integer arrays
- uint64: 64-bit unsigned integer arrays
4*pi
int8(200)+int8(300)
four times pi here has one operator multiplication, also known as times and two operations four and pi. Because they're too operands, we call this a binary operation. The type of each of these operations is double, which is the default and MATLAB. But you can specify different types by using MATLABs casting functions like at eight for example.
That's because 500 is too large to fit into a little old and eight. And so the result we get is the closest number to 500 that lies inside the range of numbers that do fit into an innate, which goes from minus 128 to plus 127.
So far, all our operations have used operations of the same type. So none of these expressions is an example of mixed mode arithmetic. We'll get there, but in the meantime, there's another way not to have operations with mixed types, and that's just a have one operand like this.
-3
We still have one operator, the minus, but only one operand the three This is called a unitary operation.
here is a table showing the shape compatibility rules for binary operators.
Here we have five rules for a binary operation, which we show here as an operand x and operator op and a second operand y. Where x and y could be any scaler's or two dimensional arrays who shapes are compatible for the operator op. For example, plus minus, and all the dot operators which are known collectively, is array operators require that the two operations x and y have the same size and shape.
For example, plus minus, and all the dot operators which are known collectively, is array operators require that the two operations x and y have the same size and shape.
Rule B is for multiplication, also known as matrix multiplication. And it says that the size of the second dimension of x must equal the size of the first dimension of y.
Or to put it another way, the number of columns of x must equal the number of rows of y. Here's an example that follows that rule.Yeah, it follows it, because the first operandi has three columns in the second operandi has three rows.
[1 2 3;3 4 5]*[6;7;8]
if weswop will get error.
The last rule E for the exponentiation operator, also known as the power operator, says that both operations must be square. So for example, 2 buy 2 is fine, but 2 by 3 is not, and one or both of them must be scaler.
Let's try it for a three by three matrix raised to a scaler power.
[1 2 3;2 5 8;9 6 3]^2
but if do non square matrix to a power-
it will give error.
So the last rule required that one of the operations be a scaler. And on the subject of scaler, I need to point out that the other four rules include special cases for scaler is that we haven't mentioned yet. We had those special cases.
The rules look like this.
The special cases are that if one operand is a scaler, the other operand could be any shape it all. The operator is applied to the scaler and each element in the array, with the only stipulation being that for the two slash operators, rule C and D scaler must be in the denominator These five rules have been part of my lab for decades but in September of 2016, when version 2016b appeared.
It's rule F here, which says that you can now add vectors to arrays or subtract vectors from arrays. Or perform any array operation for that matter not just any vector in any array though. You can only do it if the vector is either a row vector with a length equal to the row length of the array. Or a column vector with the length equal to the column length of the array.
Script examples of binary arithmetic operations
A = [100 200 300; 400 500 600]
c = [7;9]
A_plus_c = A + c
it added the column vector to each column of the array.7 was added to every element on the first row, and 9 was added to every element on the second row.
if you're driving an old installed model older than 2016b, you'll get an error message that says matrix dimensions must agree. But don't despair, you can accomplish the same thing by using the built in function, repmat, to make an array of copies of the vector.
C = repmat(c,1,3)
now we can add
A+C
Here according rule 6-
rule-2 and 5
int8(123) - int8(40)
uint16([1 2; 3 4]) + uint16([64 28;10 55])
%uint16([1 2; 3 4])* uint16([64 28;10 55])
uint16([1 2; 3 4])*uint16(64)
uint16(4)*uint16([64 28; 10 55])
uint16([1 2; 3 4])*64
4*uint16([64 28; 10 55])
single(1) + double(2)
int64(3)^2
%int64(9)^0.5
%sqrt(int64(9))
as matlab has to give whole no in output (as above example int64 9) but fractional power dont give whole no so cause error.
In fact, there is just one rule. The type of the output of any legal arithmetic operation is the same as the type of the operation that occupies the least space and memory.
So, for example, if we had an 8 bit integer to a double, the result is an 8 bit integer. Because an 8 bit integer occupies less space than a double. Which you may remember uses 64 bits.
Or say we multiply an array of singles by an array of doubles. And the type of the output is single because a single occupies only 32 bits.
Extending the rule to non mixed mode arithmetic. When both operands have the same type. Then, of course, the output has that same type too.
n = int16(9876)
x = 12
So here we've added a double to a 16 bit integer. Since the 16 bit integer occupies only 16 bits, while the double occupies 64 bits, the result is an int6. Here's more operations. These are all mixed mode arithmetic operations because the operations have different types. And in every case we get an int 16.
x + n
n*x
n/x
n^x
x/n
as int cant holde fraction.
x/9876
here it eill give ans in fraction.
So why does MATLAB choose the smaller type for its result? Well, that's a very good question. As a matter of fact, most other computer programming languages do give you a double when you mix a double in an integer. But MATLAB has a very good answer to this very good question. It produces an integer to save memory.
Let's see an example of how MATLAB approach saves memory. I have an image of the MATLAB logo saved as a PNG file here in this folder.
Let's read that into a variable and display it.
clear;
M=imread('matlab.png');
imshow(M)
As you can see over here in the workspace, the variable M,
which contains the image, is a 400 by 400 by 3 array, meaning that it has 400 columns, 400 rows and 3 pages. Each page of the array contains a 400 by 400 array of color intensities. One page each for the red, green and blue colors, also known as an RGB image, in which each set of red, green and blue values makes up one pixel.
What's most important to us though here is that the array is of type Uint8.
We can calculate how much memory space this image requires, but let's take the easy way and let the function whos tell us, 480,000 bytes.
whos
Now, let's do a little image processing on it. I'll create a darker version of the image by dividing all the intensity values by three.
D=M/3;
imshow(D)
whos
So the size of the image has not changed.
480,000 bytes for each of them.
That's because of MATLAB rule that says that the type of the output of the arithmetic operation that produced D is the same is the type of its smallest operand.
The two operands were M, which is a Uint8 and three, which is a double, so the result is a Uint8. If we were using a language like say, C or C++ or Java, which would produce a double instead of Uint8 as output, the result would be eight times as big. Here's the equivalent operation in those languages.
D=double(M)/3;
What we've done is converted the eight bit M into a 64 bit double.
So now the type of the operand that was taking less space has been changed to match the type of the operand, and that takes more space. This step, which is done before the arithmetic operation is applied, is called widening and widening is exactly what is done by C, C++ and Java from mixed mode arithmetic operations. So let's see how big D is now.
whos
Almost four megabytes. You may think that four megabytes is nothing these days, and you would be right, but once you start dealing with medical images such as MRI or CT, which can consist of over 100 individual images. Or if you think of videos that can have 30 or even 60 frames per second. All of a sudden we're talking about gigabytes and a factor of it becomes crucial. MATLABs approach, by comparison, is to use narrowing like this.
D=M/uint8(3);
So now the type of the operation which takes more space is changed to match the type of one that takes less space, thereby saving memory. And it's not just a matter of saving memory, operations on wider operand take more time too. So MATLABs approach of narrowing in mixed mode arithmetic saves both space and time. The downside is that during narrowing values around it off and you'll find people online railing away about that, but in fact the difference of 0.5 is rarely even detectable in an image.
And you can always override this behavior by explicitly widening the smaller operand.
If you do wide and make sure you widen the operand and as we have done and not the result of the operation, if you widen the result of the operation, you could lose accuracy.
Here's a simpler example to show what happens.
a=int8(17)
b=double(a)/2
c=double(a/2)
So, B and C have the same type double, but they have different values. Do you see why?
Well, it's because for B, we widen the operand and for c, we widened the result of the operation. When we calculate B here, we first convert A to a double and then divide by 2.
And since 2 is by default, also a double, the results of the operation is a double, but when we calculate C, we first divide by 2.
And since a is an int8, the result is an int8, which means that the exact answer 8.5 is rounded to the end of your 9. We then convert that 9 from an int8 to a double.
And now let's see what happens if we use an int8 type for the 2, as well as for the A.
f=int8(2)
b=double(a)/f
c=double(a/f)
This time B and C have different types, but the same value.
Well, here again for B, we widen the operand and for C, we widen the result of the operation. But this time since F is an int8, the result for B is an int8, so the result is rounded to 9.
Summary